package cn.topcodes.tcsf.admin.commons.query;

import cn.topcode.unicorn.utils.DynamicQueryUtil;
import cn.topcodes.tcsf.admin.commons.domain.Page;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.entity.Example;

import javax.persistence.EntityNotFoundException;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 单表/视图 查询服务
 * 若实体对应数据库视图，直接继承此类即可拥有查询功能
 * 视图查询一般已是多表关联查询的结果
 * 若还需要关联查询则在之类中自行扩展
 * @author unicorn
 *
 * @param <T>	实体对象泛型
 */
@Transactional(readOnly=true)
public abstract class QueryService<T> implements IQueryService<T> {
	
	@Autowired
	private Mapper<T> mapper;


	private Object lock = new Object();

	@SuppressWarnings("unchecked")
	private Class <T> entityClass = null;

	protected Mapper<T> getMapper() {
		return this.mapper;
	}

	@Override
	public Class<T> getEntityClass() {
		if(entityClass == null) {
			synchronized (lock) {
				if(entityClass == null) {
					entityClass = getEntityClazz();
				}
			}
		}
		return entityClass;
	}

	protected Class<T> getEntityClazz() {
		return (Class <T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
	}
	
	@Override
	public Example buildExample() {
		Example example = new Example(this.getEntityClass());
		return example;
	}

	@Override
	public T findOne(Long id) {
		Assert.notNull(id);
		return this.mapper.selectByPrimaryKey(id);
	}
	
	@Override
	public T findOne(Example example) {
		Assert.notNull(example);
		RowBounds rowBounds = new RowBounds(0, 1);
		List<T> list = this.mapper.selectByExampleAndRowBounds(example, rowBounds);
		if(list.isEmpty()) {
			return null;
		}
		return list.get(0);
	}
	
	@Override
	public List<T> findAll() {
		return this.mapper.selectAll();
	}
	
	@Override
	public List<T> findAll(Example example) {
		example = example == null ? this.buildExample() : example;
		return this.mapper.selectByExample(example);
	}
	
	@Override
	public Page<T> findPage(Example example, int pageNumber, int pageSize) {
		example = example == null ? this.buildExample() : example;
		pageNumber = pageNumber < 1 ? 1 : pageNumber;
		pageSize = pageSize < 1 ? 1 : pageSize;
		RowBounds rowBounds = new RowBounds((pageNumber - 1) * pageSize, pageSize);
		List<T> list = this.mapper.selectByExampleAndRowBounds(example, rowBounds);
		int total = this.mapper.selectCountByExample(example);
		return new Page<T>(list, total, pageNumber, pageSize);
	}
	
	@Override
	public Page<T> findPage(Map<String,String[]> searchParams, int pageNumber, int pageSize) {
		searchParams = searchParams == null ? new HashMap<String,String[]>() : searchParams;
		pageNumber = pageNumber < 1 ? 1 : pageNumber;
		pageSize = pageSize < 1 ? 1 : pageSize;
		Example example = this.buildExample();
		DynamicQueryUtil.convert(searchParams, example);
		return this.findPage(example, pageNumber, pageSize);
	}

	@Override
	public List<T> findAll(Map<String, String[]> searchParams) {
		searchParams = searchParams == null ? new HashMap<String,String[]>() : searchParams;
		Example example = this.buildExample();
		DynamicQueryUtil.convert(searchParams, example);
		return this.findAll(example);
	}

	@Override
	public int countAll() {
		return this.mapper.selectCountByExample(this.buildExample());
	}

	@Override
	public boolean exist(Long id, String property, Object value) {
		Example example = this.buildExample();
		Example.Criteria criteria = example.createCriteria();
		criteria.andEqualTo(property,value);
		if(id != null) {
			criteria.andNotEqualTo("id",id);
		}
		int count = this.count(example);
		return count > 0;
	}

	@Override
	public int count(Example example) {
		example = example == null ? this.buildExample() : example;
		return this.mapper.selectCountByExample(example);
	}

	@Override
	public T findOneOrFail(Long id) {
		T t = this.findOne(id);
		if(t == null) {
			throw new EntityNotFoundException("资源不存在");
		}
		return t;
	}

	@Override
	public T findOneOrFail(Example example) {
		T t = this.findOne(example);
		if(t == null) {
			throw new EntityNotFoundException("资源不存在");
		}
		return t;
	}

	@Override
	public boolean exist(Long id) {
		return exist(null, "id", id);
	}

	@Override
	public boolean exist(String property, Object value) {
		return exist(null, property, value);
	}

	@Override
	public boolean exist(Example example) {
		int count = this.count(example);
		return count > 0;
	}
}
